﻿using System;
using System.Collections.Generic;
using LevelEditor.Forms;

namespace LevelEditor.View
{

    public class HistoryManager
    {
        #region Fields

        MainForm mainForm;

        #endregion

        #region Properties

        public List<IDoUndo> TimeLine { get; private set; }

        public int CurrentActionIndex { get; private set; }

        #endregion

        #region Constructors

        public HistoryManager(MainForm form)
        {
            mainForm = form;
            TimeLine = new List<IDoUndo>();
            TimeLine.Add(new DoUndoNothing(mainForm));
        }

        #endregion

        #region Event Handlers

        public delegate void ChangedEventHandler(object sender, EventArgs e);
        public event ChangedEventHandler Changed;
        protected virtual void OnChanged(EventArgs e)
        {
            if (Changed != null)
                Changed(this, e);
        }

        public delegate void AddActionEventHandler(object sender, EventArgs e);
        public event AddActionEventHandler AddAction;
        protected virtual void OnAddAction(EventArgs e)
        {
            if (AddAction != null)
                AddAction(this, e);
        }

        public delegate void ClearEventHandler(object sender, EventArgs e);
        public event ClearEventHandler Cleared;
        protected virtual void OnClear(EventArgs e)
        {
            if (Cleared != null)
                Cleared(this, e);
        }

        #endregion

        #region Methods

        /// <summary>
        /// Add and perform the given action.
        /// </summary>
        /// <param name="action">The given action.</param>
        public void AddAndDo(IDoUndo action)
        {
            if (action.Do())
            {
                if (CurrentActionIndex < TimeLine.Count - 1)
                {
                    TimeLine.RemoveRange(CurrentActionIndex + 1, TimeLine.Count - (CurrentActionIndex + 1));
                }
                if (action.ToString() != TimeLine[CurrentActionIndex].ToString())
                {
                    CurrentActionIndex = TimeLine.Count;
                    TimeLine.Add(action);
                    OnAddAction(EventArgs.Empty);
                    OnChanged(EventArgs.Empty);
                }
            }
        }

        /// <summary>
        /// Travel through time and maybe space.  Sets present to some point in the past or future.
        /// </summary>
        /// <param name="index">Index of action in TimeLine to go back/forward to.</param>
        public void SetCurrentActionTo(int index)
        {
            if (index < TimeLine.Count && index >= 0)
            {
                while (CurrentActionIndex < index)
                {
                    ++CurrentActionIndex;
                    TimeLine[CurrentActionIndex].Do();
                }
                while (CurrentActionIndex > index)
                {
                    TimeLine[CurrentActionIndex].Undo();
                    --CurrentActionIndex;
                }
            }
            OnChanged(EventArgs.Empty);
        }

        /// <summary>
        /// Oops!
        /// </summary>
        public void Undo()
        {
            if (CurrentActionIndex > 0 && TimeLine.Count > 1)
            {
                TimeLine[CurrentActionIndex--].Undo();
            }
            OnChanged(EventArgs.Empty);
        }

        /// <summary>
        /// Do that again!
        /// </summary>
        public void Redo()
        {
            if (CurrentActionIndex < TimeLine.Count - 1)
            {
                TimeLine[CurrentActionIndex++].Do();
            }
            OnChanged(EventArgs.Empty);
        }

        /// <summary>
        /// Wipe out all of known history.  Can NOT be undone.
        /// </summary>
        public void Clear()
        {
            TimeLine.Clear();
            TimeLine.Add(new DoUndoNothing(mainForm));
            CurrentActionIndex = 0;
            OnClear(EventArgs.Empty);
            OnChanged(EventArgs.Empty);
        }

        #endregion
    }
}
